package edu.ynu.se.xiecheng.achitectureclass.service;

import edu.ynu.se.xiecheng.achitectureclass.dao.*;
import edu.ynu.se.xiecheng.achitectureclass.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;

@Service
public class SensorDataService {
    private final String FBOX_LOGIN_URL = "https://fbox360.com/idserver/core/connect/token";
    private final String FBOX_DATA_URL = "https://fbox360.com/api/v2/dmon/value/get";

    @Autowired
    private EquipmentDao equipmentDao;

    @Autowired
    private MoistureReadingDao moistureReadingDao;

    @Autowired
    private TemperatureReadingDao temperatureReadingDao;

    @Autowired
    private NitrogenLevelDao nitrogenLevelDao;

    @Autowired
    private PhosphorusLevelDao phosphorusLevelDao;

    @Autowired
    private PotassiumLevelDao potassiumLevelDao;

    private String accessToken;
    private LocalDateTime tokenExpireTime;

    private String getAccessToken() {
        if (accessToken != null && LocalDateTime.now().isBefore(tokenExpireTime)) {
            return accessToken;
        }

        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("scope", "fbox");
        map.add("client_id", "68db2e8bda8d47b5b5db7eaf71c7dcdd");
        map.add("client_secret", "76dc724c95004acab25482d344dab407");
        map.add("grant_type", "client_credentials");

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

        ResponseEntity<Map> response = restTemplate.postForEntity(FBOX_LOGIN_URL, request, Map.class);
        Map<String, Object> body = response.getBody();

        accessToken = (String) body.get("access_token");
        int expiresIn = (Integer) body.get("expires_in");
        tokenExpireTime = LocalDateTime.now().plusSeconds(expiresIn);

        return accessToken;
    }

    public void collectSensorData(String boxNo, List<String> sensorIds) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getAccessToken());

        Map<String, List<String>> requestBody = new HashMap<>();
        requestBody.put("ids", sensorIds);

        HttpEntity<Map<String, List<String>>> request = new HttpEntity<>(requestBody, headers);

        ResponseEntity<List> response = restTemplate.postForEntity(
                FBOX_DATA_URL + "?boxNo=" + boxNo,
                request,
                List.class
        );

        List<Map<String, Object>> sensorDataList = response.getBody();

        for (Map<String, Object> sensorData : sensorDataList) {
            String sensorId = (String) sensorData.get("id");
            String name = (String) sensorData.get("name");
            Double value = convertValue(((Number) sensorData.get("value")).doubleValue(), name);
            String timestamp = (String) sensorData.get("timestamp");

            // 转换时间
            LocalDateTime localDateTime = ZonedDateTime.parse(timestamp)
                    .withZoneSameInstant(ZoneId.systemDefault())
                    .toLocalDateTime();
            Date measureTime = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

            Equipment equipment = equipmentDao.findByEquipmentSerial(sensorId)
                    .orElseThrow(() -> new RuntimeException("设备未找到: " + sensorId));

            // 根据设备名称存储数据
            switch (name) {
                case "空气相对湿度":
                    saveMoistureReading(equipment, value, measureTime);
                    break;
                case "空气温度":
                    saveTemperatureReading(equipment, value, measureTime);
                    break;
                case "土壤氮含量":
                    saveNitrogenLevel(equipment, value, measureTime);
                    break;
                case "土壤磷含量":
                    savePhosphorusLevel(equipment, value, measureTime);
                    break;
                case "土壤钾含量":
                    savePotassiumLevel(equipment, value, measureTime);
                    break;
            }
        }
    }

    private Double convertValue(Double rawValue, String sensorName) {
        switch (sensorName) {
            case "空气温度":
                return rawValue / 10.0; // 277 -> 27.7℃
            case "空气相对湿度":
                return rawValue / 10.0; // 549 -> 54.9%
            default:
                return rawValue;
        }
    }

    private void saveMoistureReading(Equipment equipment, Double value, Date measureTime) {
        MoistureReading data = new MoistureReading();
        data.setEquipment(equipment);
        data.setMoistureValue(value);
        data.setRecordedTime(measureTime);
        moistureReadingDao.save(data);
    }

    private void saveTemperatureReading(Equipment equipment, Double value, Date measureTime) {
        TemperatureReading data = new TemperatureReading();
        data.setEquipment(equipment);
        data.setTemperatureValue(value);
        //
        data.setRecordedTime(measureTime);
        temperatureReadingDao.save(data);
    }

    private void saveNitrogenLevel(Equipment equipment, Double value, Date measureTime) {
        NitrogenLevel data = new NitrogenLevel();
        data.setEquipment(equipment);
        data.setNitrogenValue(value);
        //
        data.setRecordedTime(measureTime);
        nitrogenLevelDao.save(data);
    }

    private void savePhosphorusLevel(Equipment equipment, Double value, Date measureTime) {
        PhosphorusLevel data = new PhosphorusLevel();
        data.setEquipment(equipment);
        data.setPhosphorusValue(value);
        data.setRecordedTime(measureTime);
        phosphorusLevelDao.save(data);
    }

    private void savePotassiumLevel(Equipment equipment, Double value, Date measureTime) {
        PotassiumLevel data = new PotassiumLevel();
        data.setEquipment(equipment);
        data.setPotassiumValue(value);
        data.setRecordedTime(measureTime);
        potassiumLevelDao.save(data);
    }

    @Scheduled(fixedRate = 1800000) // 每30分钟执行一次
    public void scheduledDataCollection() {
        try {
            // 采集NPK数据
            collectSensorData("300023040071", Arrays.asList(
                    "327061330932536077",  // 土壤氮含量
                    "327061330932536078",  // 土壤磷含量
                    "327061330932536079"   // 土壤钾含量
            ));

            // 采集温湿度数据
            collectSensorData("300023040071", Arrays.asList(
                    "327061330933584659",  // 空气温度
                    "327061330933584658"   // 空气相对湿度
            ));
        } catch (Exception e) {
            // 记录错误日志
            e.printStackTrace();
        }
    }

    /**
     * 获取最新的传感器数据
     */
    public List<Map<String, Object>> getLatestData(Long plantationId) {
        List<Map<String, Object>> result = new ArrayList<>();

        // 获取大棚下的所有设备
        List<Equipment> equipments = equipmentDao.findByPlantationId(plantationId);

        for (Equipment equipment : equipments) {
            Map<String, Object> sensorData = new HashMap<>();
            sensorData.put("type", equipment.getEquipmentType());

            switch (equipment.getEquipmentType()) {
                case "temperature":
                    TemperatureReading tempData = temperatureReadingDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                    if (tempData != null) {
                        sensorData.put("value", tempData.getTemperatureValue());
                        sensorData.put("time", tempData.getRecordedTime());
                        result.add(sensorData);
                    }
                    break;
                case "humidity":
                    MoistureReading moistureData = moistureReadingDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                    if (moistureData != null) {
                        sensorData.put("value", moistureData.getMoistureValue());
                        sensorData.put("time", moistureData.getRecordedTime());
                        result.add(sensorData);
                    }
                    break;
                case "nitrogen":
                    NitrogenLevel nitrogenData = nitrogenLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                    if (nitrogenData != null) {
                        sensorData.put("value", nitrogenData.getNitrogenValue());
                        sensorData.put("time", nitrogenData.getRecordedTime());
                        result.add(sensorData);
                    }
                    break;
                case "phosphorus":
                    PhosphorusLevel phosphorusData = phosphorusLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                    if (phosphorusData != null) {
                        sensorData.put("value", phosphorusData.getPhosphorusValue());
                        sensorData.put("time", phosphorusData.getRecordedTime());
                        result.add(sensorData);
                    }
                    break;
                case "potassium":
                    PotassiumLevel potassiumData = potassiumLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                    if (potassiumData != null) {
                        sensorData.put("value", potassiumData.getPotassiumValue());
                        sensorData.put("time", potassiumData.getRecordedTime());
                        result.add(sensorData);
                    }
                    break;
            }
        }

        return result;
    }

    /**
     * 获取历史数据
     */
    public List<Map<String, Object>> getHistoryData(String equipmentSerial, Date startTime, Date endTime) {
        Equipment equipment = equipmentDao.findByEquipmentSerial(equipmentSerial)
                .orElseThrow(() -> new RuntimeException("设备不存在"));

        if (startTime == null) {
            LocalDateTime defaultStartTime = LocalDateTime.now().minusDays(7);
            startTime = Date.from(defaultStartTime.atZone(ZoneId.systemDefault()).toInstant());
        }
        if (endTime == null) {
            endTime = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
        }

        List<Map<String, Object>> result = new ArrayList<>();

        switch (equipment.getEquipmentName()) {
            case "空气温度":
                List<TemperatureReading> tempData = temperatureReadingDao.findByEquipmentAndRecordedTimeBetweenOrderByRecordedTimeDesc(
                        equipment, startTime, endTime);
                tempData.forEach(data -> result.add(Map.of(
                        "type", "temperature",
                        "value", data.getTemperatureValue(),
                        "time", data.getRecordedTime()
                )));
                break;
            case "空气相对湿度":
                List<MoistureReading> moistureData = moistureReadingDao.findByEquipmentAndRecordedTimeBetweenOrderByRecordedTimeDesc(
                        equipment, startTime, endTime);
                moistureData.forEach(data -> result.add(Map.of(
                        "type", "humidity",
                        "value", data.getMoistureValue(),
                        "time", data.getRecordedTime()
                )));
                break;
            case "土壤氮含量":
                List<NitrogenLevel> nitrogenData = nitrogenLevelDao.findByEquipmentAndRecordedTimeBetweenOrderByRecordedTimeDesc(
                        equipment, startTime, endTime);
                nitrogenData.forEach(data -> result.add(Map.of(
                        "type", "nitrogen",
                        "value", data.getNitrogenValue(),
                        "time", data.getRecordedTime()
                )));
                break;
            case "土壤磷含量":
                List<PhosphorusLevel> phosphorusData = phosphorusLevelDao.findByEquipmentAndRecordedTimeBetweenOrderByRecordedTimeDesc(
                        equipment, startTime, endTime);
                phosphorusData.forEach(data -> result.add(Map.of(
                        "type", "phosphorus",
                        "value", data.getPhosphorusValue(),
                        "time", data.getRecordedTime()
                )));
                break;
            case "土壤钾含量":
                List<PotassiumLevel> potassiumData = potassiumLevelDao.findByEquipmentAndRecordedTimeBetweenOrderByRecordedTimeDesc(
                        equipment, startTime, endTime);
                potassiumData.forEach(data -> result.add(Map.of(
                        "type", "potassium",
                        "value", data.getPotassiumValue(),
                        "time", data.getRecordedTime()
                )));
                break;
            default:
                throw new RuntimeException("不支持的传感器类型");
        }

        return result;
    }

    /**
     * 根据序列号获取单个传感器的最新数据
     */
    public Map<String, Object> getLatestDataBySerialNumber(String equipmentSerial) {
        Equipment equipment = equipmentDao.findByEquipmentSerial(equipmentSerial)
                .orElseThrow(() -> new RuntimeException("设备不存在"));

        Map<String, Object> result = new HashMap<>();
        result.put("type", equipment.getEquipmentType());
        result.put("name", equipment.getEquipmentName());
        result.put("equipment_serial", equipment.getEquipmentSerial());

        switch (equipment.getEquipmentType()) {
            case "temperature":
                TemperatureReading tempData = temperatureReadingDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                if (tempData != null) {
                    result.put("value", tempData.getTemperatureValue());
                    result.put("time", tempData.getRecordedTime());
                }
                break;
            case "humidity":
                MoistureReading moistureData = moistureReadingDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                if (moistureData != null) {
                    result.put("value", moistureData.getMoistureValue());
                    result.put("time", moistureData.getRecordedTime());
                }
                break;
            case "nitrogen":
                NitrogenLevel nitrogenData = nitrogenLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                if (nitrogenData != null) {
                    result.put("value", nitrogenData.getNitrogenValue());
                    result.put("time", nitrogenData.getRecordedTime());
                }
                break;
            case "phosphorus":
                PhosphorusLevel phosphorusData = phosphorusLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                if (phosphorusData != null) {
                    result.put("value", phosphorusData.getPhosphorusValue());
                    result.put("time", phosphorusData.getRecordedTime());
                }
                break;
            case "potassium":
                PotassiumLevel potassiumData = potassiumLevelDao.findTopByEquipmentOrderByRecordedTimeDesc(equipment);
                if (potassiumData != null) {
                    result.put("value", potassiumData.getPotassiumValue());
                    result.put("time", potassiumData.getRecordedTime());
                }
                break;
            default:
                throw new RuntimeException("不支持的传感器类型");
        }

        return result;
    }
}